/*
	Copyright (c) 2009-2011, Stefan Webb.

	This file is part of SimpleML. Please read the file "license.txt", 
	which contains the conditions under which this software is distributed.
*/

/*!
	\file error.cpp
	\author Stefan Webb
	\brief Definition of Error class members
*/

#include "stdafx.h"

// Set creation flag
bool Error::isCreated_ = false;

// Default constructor
Error::Error()
{
	// Stop more than one instance of error class being created
	if (isCreated_) {
		(error << L"Can not create more than one instance of error class.").exception();
	}

	// Set creation flag
	isCreated_ = true;

	// Declare memory for error string and initialize.
	string_ = new wchar_t[MAXIERRORLENGTH];
	string_[0] = 0;
	length_ = 0;

	// Create error stacks
	numbers_.create(16);
	strings_.create(16);

	// Initialize warning structure
	warning_.text = 0;
	warning_.number = 0;

	// Initialize flags
	isDisplay_ = true;
	isDisplayNumber_ = true;
}

// Default destructor
Error::~Error()
{
	// Delete warning string if set.
	if (warning_.text)
		delete [] warning_.text;

	// Set created flag to false to allow the creating of another Error object.
	isCreated_ = false;

	// Delete currently set but not flushed error string.
	delete[] string_;
}

// Stream-like operator, to fill error message with ANSI string
Error& Error::operator<<(const char* text)
{
	unsigned int i = 0;
	while (text[i] && (length_ < MAXIERRORLENGTH)) {
		wchar_t temp = (wchar_t) text[i++];
		string_[length_++] = temp;
	}
	string_[length_] = 0;
	return *this;
}

// Stream-like operator, to fill error message with ANSI string
Error& Error::operator<<(const unsigned char* text)
{
	return (*this << (char*) text);
}

// Stream-like operator, to fill error message with Unicode string
Error& Error::operator<<(const wchar_t* text)
{
	unsigned int i = 0;
	while (text[i] && (length_ < MAXIERRORLENGTH)) {
		string_[length_++] = text[i++];
	}
	string_[length_] = 0;
	return *this;
}

// Stream-like operator, to fill error message with integer converted to string
Error& Error::operator<<(const int number)
{
	char buffer [33];
	itoa(number, buffer, 10);
	return (*this << buffer);
}

// Displays the error message
void Error::display(bool isWarning)
{
	// Only display if display flag is set
	if (isDisplay_) {
		// Display "Warning" or "Exception" as appropriate
		if (isWarning)
			console << "Warning";
		else
			console << "Exception";

		// Display number if number flag is set and error number is not zero.
		if (isDisplayNumber_ && number_) {
			console << " " << number_;
		}

		// Print error message
		console << ": " << string_ << endl;
	}
}

// Displays error message, then exits.
void Error::exception()
{
	exception(0);
}

// Displays error message with number, then exits
void Error::exception(unsigned int number)
{
	// Set error number
	number_ = number;

	// Display error
	display(false);

	// Wait for user input
	//cin.get();

	// Exit program. Will this call destructors??? If not, then memory will be leaked...
	exit(EXIT_FAILURE);
}

// Display error message with no number (i.e. number is zero), and push onto stack.
void Error::warning()
{
	warning(0);
}

// Display error message with number and push onto stack.
void Error::warning(unsigned int number)
{
	// Set error number
	number_ = number;

	// Display
	display(true);

	//Push warning onto stack
	numbers_.push(number);
	strings_.push(string_);

	// Create new error string
	string_ = new wchar_t[MAXIERRORLENGTH];
	string_[0] = 0;
	length_ = 0;
}

/* Clear error status */
void Error::clear()
{
	// Reset error stacks
	numbers_.reset();
	strings_.reset();
}

/*! Check if there are warnings on the stack. */
bool Error::occurred()
{
	return numbers_.size() != 0;
}

/* Get the most recent warning number and message. */
Warning* Error::last()
{
	// Free memory from previous warning string
	if (warning_.text)
		delete [] warning_.text;

	// Return empty warning if no warnings
	if (!occurred()) {
		warning_.number = 0;
		warning_.text = 0;
	}
	
	// Otherwise, get warning number and string
	else {
		warning_.number = numbers_.pop();
		warning_.text = strings_.pop();
	}

	// Return warning
	return &warning_;
}

/* Control whether error or warning number is displayed. */
void Error::toggleDisplayNumber()
{
	isDisplayNumber_ = ~(unsigned int)isDisplayNumber_ & 1;
}

/* Control whether error message is displayed at all. */
void Error::toggleDisplay()
{
	isDisplay_ = ~(unsigned int)isDisplay_ & 1;
}